/*******************************************************************************
 * Copyright (c) 2007 University of Oslo.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     University of Oslo - initial API and implementation
 *******************************************************************************/

package com.cea.papyrus.diagram.sequence.sedi.model.command;

import static com.cea.papyrus.diagram.sequence.sedi.model.util.ModelUtil.createUniqueName;
import static com.cea.papyrus.diagram.sequence.sedi.model.util.ModelUtil.getArguments;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.gef.commands.Command;
import org.eclipse.uml2.uml.Message;
import org.eclipse.uml2.uml.NamedElement;
import org.eclipse.uml2.uml.OccurrenceSpecification;
import org.eclipse.uml2.uml.OpaqueExpression;
import org.eclipse.uml2.uml.UMLFactory;
import org.eclipse.uml2.uml.ValueSpecification;

import com.cea.papyrus.diagram.sequence.sedi.editor.message.MessageHelper;
import com.cea.papyrus.diagram.sequence.sedi.model.util.OpaqueExpressionUtil;

/**
 * Command to set the signature of a Message.
 * <p>
 * The following attributes must be set for the command to complete
 * successfully:
 * </p>
 * <ul>
 * <li>Message
 * <li>Signature - if null, it means any existing type will be unset.
 * <li>ArgumentValues - any arguments for the message
 * </ul>
 * <p>
 * The following attributes can be read to access the result of the command:
 * </p>
 * <ul>
 * <li>None
 * </ul>
 * <p>
 * <b>Known limitations</b> The signature is silently ignored if the signature
 * is incompatible with the message or if the message does not have any message
 * ends.
 * </p>
 * 
 * @author frankd@ifi.uio.no
 */
public class SetSignatureCommand extends Command {

	private Message message;
	private NamedElement /* oldSignature, */signature;
	private String[] argumentValues;
	private List<ValueSpecification> oldArguments;
	// private Event oldSendEvent, oldReceiveEvent;
	// private org.eclipse.uml2.uml.Package oldSendOwner, oldReceiveOwner;
	private EventPosition oldSendEvent, oldReceiveEvent;

	public SetSignatureCommand() {
	}

	public SetSignatureCommand(final Message message) {
		this(message, null);
	}

	public SetSignatureCommand(final Message message,
			final NamedElement signature) {
		setMessage(message);
		setSignature(signature);
	}

	public void setMessage(final Message message) {
		this.message = message;
	}

	public void setSignature(final NamedElement signature) {
		this.signature = signature;
	}

	public void setArgumentValues(final String[] arguments) {
		this.argumentValues = arguments;
	}

	@Override
	public void execute() {
		// oldSignature = message.getSignature();

		MessageHelper helper = MessageHelper.select(message.getSendEvent());
		if (message.getSendEvent() != null)
			oldSendEvent = helper.removeMessage(message.getSendEvent());
		helper.sendMessage(message.getSendEvent(), signature);

		helper = MessageHelper.select(message.getReceiveEvent());
		if (message.getReceiveEvent() != null)
			oldReceiveEvent = helper.removeMessage(message.getReceiveEvent());
		helper.receiveMessage(message.getReceiveEvent(), signature);

		List<ValueSpecification> oldArgList = getArguments(message);
		oldArguments = new ArrayList<ValueSpecification>(oldArgList.size());
		oldArguments.addAll(oldArgList);
		oldArgList.clear();

		if (argumentValues != null) {
			for (String arg : argumentValues) {
				OpaqueExpression expr = UMLFactory.eINSTANCE
						.createOpaqueExpression();
				expr
						.setName(createUniqueName(getArguments(message),
								"Argument_"));
				OpaqueExpressionUtil.addBody(expr, arg);
				getArguments(message).add(expr);
			}
		}
	}

	@Override
	public void undo() {
		EventPosition undoEvent;

		if (message.getSendEvent() instanceof OccurrenceSpecification) {
			OccurrenceSpecification occ = (OccurrenceSpecification) message
					.getSendEvent();
			if (occ.getEvent() != null) {
				undoEvent = new EventPosition(occ.getEvent());
				undoEvent.removeEvent();
			}
			if (oldSendEvent != null)
				occ.setEvent(oldSendEvent.getEvent());
		}
		if (oldSendEvent != null)
			oldSendEvent.addEvent();

		if (message.getReceiveEvent() instanceof OccurrenceSpecification) {
			OccurrenceSpecification occ = (OccurrenceSpecification) message
					.getReceiveEvent();
			if (occ.getEvent() != null) {
				undoEvent = new EventPosition(occ.getEvent());
				undoEvent.removeEvent();
			}
			if (oldReceiveEvent != null)
				occ.setEvent(oldReceiveEvent.getEvent());
		}
		if (oldReceiveEvent != null)
			oldReceiveEvent.addEvent();

		getArguments(message).clear();
		getArguments(message).addAll(oldArguments);

		oldSendEvent = null;
		oldReceiveEvent = null;
		// oldSignature = null;
		oldArguments = null;
	}

	@Override
	public void redo() {
		this.execute();
	}

	@Override
	public void dispose() {
		message = null;
		signature = null;
		argumentValues = null;
		// oldSignature = null;
		oldArguments = null;
	}
}
